/** @file bmp.c 
 * Implementation der Bitmap Funktionen
 * 
 * @author Daniel Kummer, Sebastian Kummer
 * @date Donnerstag, Dezember 12, 2007
 * @version 1.0
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include "lcv-config.h"
#include "dip.h"
#include "bmp.h"
#include "bmp-header.h"

/**
 * Holt einen 4-Byte Wert aus einem Header.
 * Diese Methode wird nur in bmp.c verwendet.
 * @param header Header
 * @param offset Offset
 * @return Wert
 */
int get_a_4byte_value(unsigned char * header, int offset);

/**
 * Setzt einen 4-Byte Wert in einem Header.
 * Diese Methode wird nur in bmp.c verwendet.
 * @param header Header
 * @param offset Offset
 * @param value Wert
 */
void set_a_4byte_value(unsigned char * header, int offset, int value);

char lcv_write_bmp(const lcv_picture pic, const char* filename)
{	
	lcv_picture * work_pic;
	lcv_picture clone;
	
	if(pic.type != IMG_BGR){
		clone = lcv_gray2rgb(&pic);
		work_pic = &clone;
	}else{
		clone = lcv_clone_picture(&pic);
		work_pic = &clone;
	}
	
	unsigned char * bmp_data;
	int pic_file;
		
	bmp_data = (unsigned char *)malloc(BMP_HEADER_LENGTH + work_pic->length);	
		
	(void)memcpy(bmp_data, bmphead_bgr, BMP_HEADER_LENGTH); 
	
	lcv_set_height(bmp_data, work_pic->height);
	lcv_set_width(bmp_data, work_pic->width);
	
	(void)memcpy((unsigned char *)(bmp_data + BMP_HEADER_LENGTH), work_pic->data, work_pic->length); 
					
	/* Bild schreiben */
	pic_file = open(filename , O_WRONLY | O_CREAT | O_TRUNC, 0666);
	if (pic_file == -1) {
		printf("Error: Could not open File for writing\n");
		exit(-1); /*schwerer Fehler */
	}
	
	write(pic_file, bmp_data, BMP_HEADER_LENGTH + work_pic->length);
	close(pic_file);
	free(bmp_data);
	lcv_free_picture(work_pic);
	
	return TRUE;
}

lcv_picture lcv_read_bmp(const char* filename)
{	
	unsigned char* buffer; 
	int height;
	char is_reverse = FALSE;
	int pic_file;
	lcv_picture res_pic;
	 	 
	buffer = (unsigned char *)malloc(BMP_HEADER_LENGTH);
	
	pic_file = open(filename, O_RDONLY, 0);
	if (pic_file == -1) {
		free(buffer);
		printf("Error: Could not open File\n");
		exit(-1); /*schwerer Fehler */
	}
	
	/* Header einlesen */
	read(pic_file, buffer, BMP_HEADER_LENGTH);
	close(pic_file);
	
	height = lcv_get_height(buffer);
	if (height < 0) {
		height = height * (-1);
		is_reverse = TRUE;
	}		
		
	res_pic.height = (unsigned short)height;
	res_pic.width  = (unsigned short)lcv_get_width(buffer);
	res_pic.length = res_pic.height * res_pic.width * 3;
	res_pic.type = IMG_BGR;
		
	int i;
		
	if (lcv_get_data_offset(buffer) == BMP_HEADER_LENGTH) {		
		if (lcv_get_colordepth(buffer) == 24) {							
			/* Free Header */
			free(buffer); 
				
			/* Speicher für Bilddaten allozieren */
			buffer = (unsigned char *)malloc(res_pic.length + BMP_HEADER_LENGTH);

			pic_file = open(filename, O_RDONLY, 0);
			if (pic_file == -1) {
				exit(-1); /*schwerer Fehler */
			}
				
			/* Bilddaten lesen */
			read(pic_file, buffer, res_pic.length + BMP_HEADER_LENGTH);
			close(pic_file);
				
			res_pic.data = (unsigned char *)malloc(res_pic.length);
				
			(void)memcpy(res_pic.data, buffer + BMP_HEADER_LENGTH, res_pic.length);
				
			free(buffer);	
												
			if (is_reverse) {								
				buffer = (unsigned char *)malloc(res_pic.length);		
								
				for (i=0; i<res_pic.height; i++) {
					(void)memcpy(buffer + (i * res_pic.width), res_pic.data + res_pic.length - ((i+1) * res_pic.width), res_pic.width);
				}
					
				(void)memcpy(res_pic.data, buffer, res_pic.length);
				free(buffer);
			}			
				
		} else {
			printf("Error: The color-depth must be 24\n");
			exit(-1);
		}		
	} else {
		printf("Error: The Bitmap-Header must be 54 Bytes (uncompressed and without colortable)\n");
		exit(-1);
	}	
	return res_pic;
}

int lcv_get_data_offset(unsigned char * header)
{
	return get_a_4byte_value(header, 10);
}

void lcv_set_data_offset(unsigned char * header, int offset)
{
	set_a_4byte_value(header, 10, offset);	
}

int lcv_get_height(unsigned char * header)
{
	return get_a_4byte_value(header, 22);
}

void lcv_set_height(unsigned char * header, int height)
{
	set_a_4byte_value(header, 22, height);	
}

int lcv_get_width(unsigned char * header)
{
	return get_a_4byte_value(header, 18);
}

void lcv_set_width(unsigned char * header, int width)
{
	set_a_4byte_value(header, 18, width);	
}

int lcv_get_colordepth(unsigned char * header)
{
	int value = 0;
	
	value += header[29];
	value = value << 8;
	value += header[28];
	
	return value;
}

int get_a_4byte_value(unsigned char * header, int offset)
{
	int value = 0;
	
	value += header[offset+3];
	value = value << 8;
	value += header[offset+2];
	value = value << 8;
	value += header[offset+1];
	value = value << 8;
	value += header[offset];
	
	return value;
}

void set_a_4byte_value(unsigned char * header, int offset, int value)
{
	int tmp = value;
	int value3, value2, value1, value0;
		
	value3 = tmp >> 24;
	header[offset+3] = value3;
	tmp = value - (value3 << 24);
	value2 = tmp >> 16;
	header[offset+2] = value2;
	tmp = value - (value3 << 24) - (value2 << 16);
	value1 = tmp >> 8;
	header[offset+1] = value1;
	value0 = value - (value3 << 24) - (value2 << 16) - (value1 << 8);
	header[offset] = value0;
}
